
//3维向量，提供内积、外积正规化方法,因为它有3个值，所以也可以当Pointer3D用，
package kgame5.k3d.core.math {
import kgame5.kmath.MathDG;
import kgame5.kmath.TriMath;

public class Vector3D {
	public static const tol : Number = 0.00001;//要是小于TOL的数就认为是0
	public static const UP:Vector3D=new Vector3D(0,1,0);
	//______________________Property_________________________
	public var 
		x : Number = 0, 
		y : Number = 0,
		z : Number = 0;
	
	//______________________Method____________________
	public function Vector3D(x : Number=0, y : Number=0, z : Number=0) {
		fill(x,y,z);
	}
	
	public function toString() : String{
		return " x:"+x+" y:"+y+" z:"+z;
	}
	public function fill(x : Number, y : Number, z : Number):void {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	//复制一个自己，很有用的东西哦
	public function clone() : Vector3D{
		return new Vector3D(x,y,z);
	}
	public function copy(v3d : Vector3D):void{
		x=v3d.x;
		y=v3d.y;
		z=v3d.z;
	}
	//计算量值
	public function magnitude() : Number {
		return Math.sqrt(x * x + y * y + z * z);
	}
	//计算量值的平方,为了判断距离而用
	public function magnitude2() : Number {
		return x * x + y * y + z * z;
	}
	//正规化
	public function normalize():void {
		var m : Number = magnitude();
		if (m <= tol) {
			m = 1;
		}
		x /= m;
		y /= m;
		z /= m;
	
		if (Math.abs(x) < tol) {
			x = 0;
		}
		if (Math.abs(y) < tol) {
			y = 0;
		}
		if (Math.abs(z) < tol) {
			z = 0;
		}
	}
	
	//取得向量投影在xoz平面上的方向,正z轴到正x轴是0~90
	public function getXOZAngle():Number{
		return MathDG.AAA2(z, x);
	}
	
	//
	public function reverse() {
		x = -x;
		y = -y;
		z = -z;
	}
	
	//判断是否为0，在lookAt()里还是有用的，用来判断当n=0或u=0时，即焦点和自己的位置重合、视线方向和头顶方向重合，这种情况下则不设置新的lookAt,仍使用以前的即可
	public function isZero() : Boolean {
		/*
		if (x == 0 && y == 0 && z == 0) {
			return true;
		} else {
			return false;
		}
		//*/
		//*
		if (Math.abs(x) < tol && Math.abs(y) < tol && Math.abs(z) < tol) {
			return true;
		}
		return false;
		//*/
	}
	//判断是否相等
	public function isEqual(a : Vector3D) : Boolean{
		if(a.x==x&&a.y==y&&a.z==z){
			return true;
		}else{
			return false;
		}
	}
	//判断是否相近,用的是一个方块而不是一个圆,这样速度要快些
	public function isNear(a : Vector3D, M : Number) : Boolean{
		if(Math.abs(a.x-x)<M 
			&& Math.abs(a.y-y)<M
			&& Math.abs(a.z-z)<M
		){
			return true;
		}else{
			return false;
		}
	}
	//
	//计算向量外积,叉积
	public function outerProduct(v : Vector3D):void {
		var x1:Number = y * v.z - z * v.y;
		var y1:Number = -x * v.z + z * v.x;
		var z1:Number = x * v.y - y * v.x;
		this.x=x1;
		this.y=y1;
		this.z=z1;
	}
	//计算向量内积，点积,结果是标量
	public function innerProduct(v : Vector3D) : Number {
		return (x * v.x + y * v.y + z * v.z);
	}
	//
	public function sub(b : Vector3D):void{
		this.x-=b.x;
		this.y-=b.y;
		this.z-=b.z;
	}
	//
	public function add(b : Vector3D):void{
		this.x+=b.x;
		this.y+=b.y;
		this.z+=b.z;
	}
	//注意如果原来就是0的话，这个乘以数之后还是0
	public function mul(s : Number):void{
		this.x*=s;
		this.y*=s;
		this.z*=s;
	}
	//
	public function div(s : Number):void{
		if(s==0){
			throw new Error("Vector3D.div!!除数为0");
		}
		this.x/=s;
		this.y/=s;
		this.z/=s;
	}
	
	public function polarXOZ(angle:Number,radius:Number):void{
		//var rad:Number=angle*MathDG.DTR;//炮弹弧度

		//x=radius*Math.sin(rad);
		//z=radius*Math.cos(rad);
		
		//快速算法
		x=radius*TriMath.sin(angle);
		z=radius*TriMath.cos(angle);
	}
	
	// 取得在xoz平面的角度
	public function angleOfXOZ():Number{
		return MathDG.AAA2(z, x);
	}
	
	//_______________________Static____________________________
	/*
	static var DTR:Number=Math.PI/180;
	public static function SRotate2D(v:Vector3D,angle:Number):Vector3D{
		var p:Point=Point.polar(v.magnitude(),angle*DTR);
		return new Vector3D(p.x,p.y,0);
	}
	 * 
	 */
	//计算向量外积,叉积
	public static function SOuterProduct(u : Vector3D, v : Vector3D, c : Vector3D):void {
		c.fill(u.y * v.z - u.z * v.y,
	           -u.x * v.z + u.z * v.x,
	           u.x * v.y - u.y * v.x);
	}
	
	//计算向量内积，点积,结果是标量
	public static function SInnerProduct(u : Vector3D, v : Vector3D) : Number {
		return (u.x * v.x + u.y * v.y + u.z * v.z);
	}
	//标量除法
	public static function SDiv(u : Vector3D, a : Number, c : Vector3D):void {
		c.fill(u.x / a, u.y / a, u.z / a);				
	}
	//标量乘法
	public static function SMul(u : Vector3D, a : Number, c : Vector3D):void {
		c.fill(u.x * a, u.y *a, u.z *a);				
	}
	
	//结果保存在c里
	public static function SSub(a : Vector3D, b : Vector3D, c : Vector3D):void{
		
		c.fill(
			a.x - b.x,
			a.y - b.y,
	        a.z - b.z
	        );
	}
	//结果保存在c里
	public static function SAdd(a : Vector3D, b : Vector3D, c : Vector3D):void{
		c.fill(a.x + b.x,
	           a.y + b.y,
	           a.z + b.z);
	}
	public static function SGetDistance(a : Vector3D, b : Vector3D) : Number{
		var dx : Number = a.x-b.x;
		var dy : Number = a.y-b.y;
		var dz : Number = a.z-b.z;

		return Math.sqrt(dx*dx+dy*dy+dz*dz);
	}
	public static function SGetDistance2(a : Vector3D, b : Vector3D) : Number{
		var dx : Number = a.x-b.x;
		var dy : Number = a.y-b.y;
		var dz : Number = a.z-b.z;

		return dx*dx+dy*dy+dz*dz;
	}
	public static function SGetXOZDistance(a : Vector3D, b : Vector3D) : Number{
		var dx : Number = a.x-b.x;
		//var dy : Number = a.y-b.y;
		var dz : Number = a.z-b.z;

		return Math.sqrt(dx*dx+dz*dz);
	}
	
	//xoz平面上根据角度和半径计算出
	public static function SPolarXOZ(angle:Number,radius:Number):Vector3D{
		var v3d:Vector3D=new Vector3D();
		
		v3d.polarXOZ(angle, radius);
		
//		var rad:Number=angle*MathDG.DTR;//炮弹弧度
//
//		v3d.x=radius*Math.sin(rad);
//		v3d.z=radius*Math.cos(rad);
		
		return v3d;
	}
	
	
	
	//判断ab的距离是大于，等于，小于Range
	public static function  SCompareDistXOZ(a :Vector3D,b:Vector3D,Range:int):int{
		
		// 判断是否被冲击波击中
		var  dx:int=a.x-b.x;
		var  dz:int=a.z-b.z;
		
		var realDist2:int = dx*dx+dz*dz;
		
		var  Range2:int=Range*Range;
		
		if(realDist2<Range2){
			return -1;
		}else if(realDist2==Range2){
			return 0;
		}else{
			return 1;
		}
	}
	
	//判断是否变了，并且把老值放到新值里去
	public static function STestDirty(curV:Vector3D,lastV:Vector3D):Boolean{
		//判断
		var bDirty:Boolean=!curV.isEqual(lastV);
		
		//保存
		if(bDirty){
			lastV.copy(curV);
		}
		
		return bDirty;
	}
}//end of class
}